package ru.dadle8.service.impl.bot;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboard;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardRemove;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow;
import ru.dadle8.model.bot.entity.MenuAction;
import ru.dadle8.model.bot.entity.MessageCallbackData;
import ru.dadle8.model.bot.entity.State;
import ru.dadle8.model.bot.entity.TrainingMenuAction;
import ru.dadle8.model.bot.type.CommandState;
import ru.dadle8.service.api.bot.KeyboardService;
import ru.dadle8.service.api.bot.MenuActionService;
import ru.dadle8.service.api.bot.Storage;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toCollection;

@Service
@RequiredArgsConstructor
@Slf4j
public class KeyboardServiceImpl implements KeyboardService {

    private final MenuActionService menuActionService;
    private final Storage storage;
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public ReplyKeyboard getDefaultKeyBoard() {
        return getReplyKeyboardMarkup();
    }

    @Override
    public ReplyKeyboard getKeyBoard(String text, Long chatId) {
        MenuAction menuAction = menuActionService.getMenuAction(text);
        if (menuAction == null) {
            return null;
        }

        // back action
        if (100 == menuAction.getActionId()) {
            menuAction = getParentMenuActionFromState(chatId);
            if (menuAction == null) {
                return getDefaultKeyBoard();
            }
        }

        // save current menu action
        storage.updateState(chatId, menuAction, CommandState.TRAINING);

        return getKeyBoardByActionType(menuAction);
    }

    @Override
    public ReplyKeyboard getKeyBoard(MenuAction menuAction, Long chatId) {
        MenuAction curentMenuAction = menuAction;
        // back action
        if (100 == menuAction.getActionId()) {
            curentMenuAction = getParentMenuActionFromState(chatId);
            if (curentMenuAction == null) {
                return getDefaultKeyBoard();
            }
        }

        // save current menu action
        storage.updateState(chatId, curentMenuAction, CommandState.TRAINING);

        return getKeyBoardByActionType(curentMenuAction);
    }

    @Override
    public ReplyKeyboard getRemoveKeyboard() {
        return ReplyKeyboardRemove.builder().removeKeyboard(true).selective(false).build();
    }

    @Override
    public InlineKeyboardMarkup getInlineKeyboardMarkup(TrainingMenuAction trainingMenuAction) {
        InlineKeyboardMarkup inlineKeyboardMarkup;
        try {
            inlineKeyboardMarkup = InlineKeyboardMarkup.builder()
                    .keyboardRow(new ArrayList<>() {{
                        // start button
                        add(InlineKeyboardButton.builder()
                                .text(trainingMenuAction.getStartMessage())
                                .callbackData(objectMapper.writeValueAsString(new MessageCallbackData(200, 0)))
                                .build());
                        // end button
                        add(InlineKeyboardButton.builder()
                                .text(trainingMenuAction.getStopMessage())
                                .callbackData(objectMapper.writeValueAsString(new MessageCallbackData(400, trainingMenuAction.getCount())))
                                .build());
                    }})
                    .build();
        } catch (JsonProcessingException e) {
            inlineKeyboardMarkup = InlineKeyboardMarkup.builder().build();
        }

        return inlineKeyboardMarkup;
    }

    @Override
    public InlineKeyboardMarkup getInlineKeyboardMarkup(String text, int actionId, int num, Long toChatId) {
        InlineKeyboardMarkup inlineKeyboardMarkup;
        try {
            inlineKeyboardMarkup = InlineKeyboardMarkup.builder()
                    .keyboardRow(new ArrayList<>() {{
                        add(InlineKeyboardButton.builder()
                                .text(text)
                                .callbackData(objectMapper.writeValueAsString(new MessageCallbackData(actionId, num, toChatId))) // answer menuActionId
                                .build());
                    }})
                    .build();
        } catch (JsonProcessingException e) {
            inlineKeyboardMarkup = InlineKeyboardMarkup.builder().build();
        }

        return inlineKeyboardMarkup;
    }

    public InlineKeyboardMarkup getInlineKeyboardMarkupWithPagination(List<String> text, List<Integer> actionIds, int num) {
        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
        try {
            int count = 0;
            List<List<InlineKeyboardButton>> rows = new ArrayList<>();
            for (int i = 0; i < 30; i++) {
                List<InlineKeyboardButton> row = new ArrayList<>();
                if (count < text.size()) {
                    row.add(InlineKeyboardButton.builder()
                            .text(text.get(count))
                            .callbackData(objectMapper.writeValueAsString(new MessageCallbackData(actionIds.get(count), num)))
                            .build());
                    count++;

                }
                rows.add(row);
            }

            List<InlineKeyboardButton> rowWithButtons = new ArrayList<>();
            rowWithButtons.add(InlineKeyboardButton.builder()
                    .text("Назад")
                    .callbackData(objectMapper.writeValueAsString(new MessageCallbackData(500, num - 1)))
                    .build());
            rowWithButtons.add(InlineKeyboardButton.builder()
                    .text("Вперед")
                    .callbackData(objectMapper.writeValueAsString(new MessageCallbackData(600, num + 1)))
                    .build());
            rows.add(rowWithButtons);
            inlineKeyboardMarkup.setKeyboard(rows);
        } catch (JsonProcessingException e) {
            log.error(e.getMessage());
        }

        return inlineKeyboardMarkup;
    }

    @Override
    public InlineKeyboardMarkup createInlineMarkup(MessageCallbackData callbackData) {
        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
        InlineKeyboardButton stopButton = new InlineKeyboardButton();
        stopButton.setText("Готово");
        try {
            stopButton.setCallbackData(objectMapper.writeValueAsString(callbackData));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        List<InlineKeyboardButton> keyboardButtonsRow2 = new ArrayList<>();
        keyboardButtonsRow2.add(stopButton);
        List<List<InlineKeyboardButton>> rowList = new ArrayList<>();
        rowList.add(keyboardButtonsRow2);
        inlineKeyboardMarkup.setKeyboard(rowList);

        return inlineKeyboardMarkup;
    }

    private ReplyKeyboard getKeyBoardByActionType(MenuAction menuAction) {
        if (menuAction.getType() == 2) {
            TrainingMenuAction trainingMenuAction = (TrainingMenuAction) menuAction;
            return getInlineKeyboardMarkup(trainingMenuAction);
        }

        return getReplyKeyboardMarkup(menuAction);
    }

    private MenuAction getParentMenuActionFromState(Long chatId) {
        MenuAction menuAction;
        State state = storage.getCurrentState(chatId);
        if (state == null) {
            return null;
        }

        MenuAction currentMenuAction = menuActionService.getMenuAction(state.menuActionId());
        if (currentMenuAction == null) {
            return null;
        }

        menuAction = menuActionService.getMenuAction(currentMenuAction.getParentActionId());
        return menuAction;
    }

    private ReplyKeyboardMarkup getReplyKeyboardMarkup() {
        return getReplyKeyboardMarkup(null);
    }

    private ReplyKeyboardMarkup getReplyKeyboardMarkup(MenuAction currentMenuAction) {
        Collection<MenuAction> childMenuActions = currentMenuAction == null ? menuActionService.getMenuActions() : currentMenuAction.getChildActions();

        Map<Integer, Set<MenuAction>> sortedMenuActions = childMenuActions.stream()
                .collect(Collectors.groupingBy(MenuAction::getRowOrder, TreeMap::new, toCollection(TreeSet::new)));
        ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup();
        List<KeyboardRow> keyboardRows = new ArrayList<>();

        for (Set<MenuAction> v : sortedMenuActions.values()) {
            KeyboardRow keyboardRow = new KeyboardRow();
            for (MenuAction menuAction : v) {
                keyboardRow.add(new KeyboardButton(menuAction.getTitle()));
            }
            keyboardRows.add(keyboardRow);
        }

        if (currentMenuAction != null) {
            KeyboardRow keyboardRow = new KeyboardRow();
            for (MenuAction menuAction : menuActionService.getMenuActionsByDefault()) {
                keyboardRow.add(new KeyboardButton(menuAction.getTitle()));
            }
            keyboardRows.add(keyboardRow);
        }

        keyboardMarkup.setKeyboard(keyboardRows);

        keyboardMarkup.setResizeKeyboard(true); //подгоняем размер
        keyboardMarkup.setOneTimeKeyboard(false); //скрываем после использования
        return keyboardMarkup;
    }
}
